#region Using References

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Dynamic;

#endregion

namespace Beaker.OpenCube.Nbt
{
	/// <summary>
	/// Represents a single object with key/value pairs of objects. 
	/// Acts just like normal Dictionary&lt;string, object&gt;, with dynamic support.
	/// </summary>
	public sealed class NbtDictionary
		: DynamicObject, IDictionary<string, object>
	{
		private readonly Dictionary<string, object> _dictionary;

		public NbtDictionary()
		{
			Contract.Ensures( _dictionary != null );
			Contract.Ensures( _dictionary.Count == 0 );
			Contract.Ensures( Count == 0 );

			_dictionary = new Dictionary<string, object>();
		}

		[ContractInvariantMethod]
		private void ContractInvariant()
		{
			Contract.Invariant( _dictionary != null );
		}

		/// <summary>
		/// Gets the number of key-value pairs in the dictionary.
		/// </summary>
		/// <value>
		/// The number of key-value pairs in the dictionary.
		/// </value>
		public int Count
		{
			get
			{
				Contract.Ensures( Contract.Result<int>() == _dictionary.Count );
				return _dictionary.Count;
			}
		}

		public override bool TryGetMember( GetMemberBinder binder, out object result )
		{
			// Contract.Requires<ArgumentNullException>( binder != null );
			return _dictionary.TryGetValue( binder.Name, out result );
		}

		public override bool TrySetMember( SetMemberBinder binder, object value )
		{
			// Contract.Requires<ArgumentNullException>( binder != null );
			Contract.Ensures( Contract.ValueAtReturn( out value ) == _dictionary[binder.Name] );
			Contract.Ensures( Contract.Result<bool>() == true, "Settings should always succeed" );
			Contract.Ensures( Count > 0, "After setting at least 1 item should be in the dictionary" );

			_dictionary[binder.Name] = value;
			return true;
		}





		IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
		{
			Contract.Ensures( Contract.Result<IEnumerator<KeyValuePair<string, object>>>() != null, "GetEnumerator should always return a value" );
			return _dictionary.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			Contract.Ensures( Contract.Result<IEnumerator>() != null, "GetEnumerator should always return a value" );
			return _dictionary.GetEnumerator();
		}

		void ICollection<KeyValuePair<string, object>>.Add( KeyValuePair<string, object> item )
		{
			Contract.Ensures( Count == Contract.OldValue( Count ) + 1, "When no exception, the count should be increased by one" );
			( (ICollection<KeyValuePair<string, object>>)_dictionary ).Add( item );
		}

		void ICollection<KeyValuePair<string, object>>.Clear()
		{
			Contract.Ensures( _dictionary.Count == 0, "After clear no items should remain in dictionary" );
			Contract.Ensures( Count == 0, "After clear no items should remain in dictionary" );

			( (ICollection<KeyValuePair<string, object>>)_dictionary ).Clear();
		}

		bool ICollection<KeyValuePair<string, object>>.Contains( KeyValuePair<string, object> item )
		{
			return ( (ICollection<KeyValuePair<string, object>>)_dictionary ).Contains( item );
		}

		void ICollection<KeyValuePair<string, object>>.CopyTo( KeyValuePair<string, object>[] array, int arrayIndex )
		{
			( (ICollection<KeyValuePair<string, object>>)_dictionary ).CopyTo( array, arrayIndex );
		}

		bool ICollection<KeyValuePair<string, object>>.Remove( KeyValuePair<string, object> item )
		{
			return ( (ICollection<KeyValuePair<string, object>>)_dictionary ).Remove( item );
		}

		int ICollection<KeyValuePair<string, object>>.Count
		{
			get
			{
				Contract.Ensures( Contract.Result<int>() == _dictionary.Count );
				return ( (ICollection<KeyValuePair<string, object>>)_dictionary ).Count;
			}
		}

		bool ICollection<KeyValuePair<string, object>>.IsReadOnly
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == false );
				return false;
			}
		}

		/// <summary>
		/// Determines whether the specified key contains key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns>
		///   <c>true</c> if the specified key contains key; otherwise, <c>false</c>.
		/// </returns>
		public bool ContainsKey( string key )
		{
			return _dictionary.ContainsKey( key );
		}

		/// <summary>
		/// Adds the specified key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="value">The value.</param>
		public void Add( string key, object value )
		{
			Contract.Ensures( Count == Contract.OldValue( Count ) + 1, "When no exception, the count should be increased by one" );
			_dictionary.Add( key, value );
		}

		/// <summary>
		/// Removes the specified key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns></returns>
		public bool Remove( string key )
		{
			Contract.Ensures( !Contract.Result<bool>() || Count == Contract.OldValue( Count ) - 1, "When return value is true (success) the count should be decreased by one" );
			return _dictionary.Remove( key );
		}

		/// <summary>
		/// Tries the get value.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="value">The value.</param>
		/// <returns></returns>
		public bool TryGetValue( string key, out object value )
		{
			Contract.Ensures( !Contract.Result<bool>() || Contract.ValueAtReturn( out key ) == null );
			Contract.Ensures( !Contract.Result<bool>() || Contract.ValueAtReturn( out value ) == null );
			return _dictionary.TryGetValue( key, out value );
		}

		/// <summary>
		/// Gets or sets the <see cref="System.Object" /> with the specified key.
		/// </summary>
		/// <value>
		/// The <see cref="System.Object" />.
		/// </value>
		/// <param name="key">The key.</param>
		/// <returns></returns>
		public object this[string key]
		{
			get
			{
				Contract.Ensures( Contract.Result<object>() == _dictionary[key] );
				return _dictionary[key];
			}
			set
			{
				Contract.Ensures( _dictionary[key] == value );
				_dictionary[key] = value;
			}
		}

		/// <summary>
		/// Gets the keys.
		/// </summary>
		/// <value>
		/// The keys.
		/// </value>
		public ICollection<string> Keys
		{
			get
			{
				Contract.Ensures( Contract.Result<ICollection<string>>() == _dictionary.Keys );
				return _dictionary.Keys;
			}
		}

		/// <summary>
		/// Gets the values.
		/// </summary>
		/// <value>
		/// The values.
		/// </value>
		public ICollection<object> Values
		{
			get
			{
				Contract.Ensures( Contract.Result<ICollection<object>>() == _dictionary.Values );
				return _dictionary.Values;
			}
		}
	}
}